package com.semanticweb.webapp.service.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Sets;
import com.semanticweb.webapp.dao.rbac.ObjectDAO;
import com.semanticweb.webapp.dao.rbac.OperationDAO;
import com.semanticweb.webapp.dao.rbac.PermissionDAO;
import com.semanticweb.webapp.dao.rbac.RoleDAO;
import com.semanticweb.webapp.dao.rbac.UserDAO;
import com.semanticweb.webapp.model.rbac.Object;
import com.semanticweb.webapp.model.rbac.Operation;
import com.semanticweb.webapp.model.rbac.Permission;
import com.semanticweb.webapp.model.rbac.Role;
import com.semanticweb.webapp.model.rbac.User;
import com.semanticweb.webapp.service.RBACService;

/**
 * {@link RBACService} implementation.
 * 
 * @author Vedran Cuca 19/2011
 * 
 */
@Service
@Transactional
public class RBACServiceImpl implements RBACService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RBACServiceImpl.class);

    @Autowired
    private OperationDAO operationDAO;
    @Autowired
    private PermissionDAO permissionDAO;
    @Autowired
    private UserDAO userDAO;
    @Autowired
    private ObjectDAO objectDAO;
    @Autowired
    private RoleDAO roleDAO;

    @Override
    public void createUsers() {
        createTestUsers(true);
        createTestUsers(false);
    }

    private void createTestUsers(final boolean isAdmin) {
        Role role = roleDAO.findByName(isAdmin ? "ADMIN" : "USER");
        if (role == null) {
            role = new Role();
            role.setRoleName(isAdmin ? "ADMIN" : "USER");
        }
        User user = userDAO.findByUsername(isAdmin ? "admin@test.com" : "user@test.com");
        if (user == null) {
            user = new User();
            user.setUsername(isAdmin ? "admin@test.com" : "user@test.com");
            user.setPassword("password");
            user.setFirstName("Test first name");
            user.setLastName("Test last name");
            user.setIsLocked(false);
        }
        final Set<User> users = new HashSet<User>();
        final Set<Role> roles = new HashSet<Role>();
        users.add(user);
        role.setUsers(users);
        roles.add(role);
        user.setRoles(roles);
        roleDAO.save(role);
    }

    @Override
    public Object addNewObject(final String objectId) {
        Object object = new Object();
        object.setName(objectId);
        final Set<Permission> objectPermissions = new HashSet<Permission>();
        Permission permission = new Permission();
        permission.setName("Permission for object " + objectId);
        Operation operation = new Operation();
        operation.setOperationName("Operation for object " + objectId);
        operation.setCanCreate(false);
        operation.setCanRead(false);
        operation.setCanUpdate(false);
        operation.setCanDelete(false);
        operation = operationDAO.save(operation);
        permission.setPermissionOperation(operation);
        permission = permissionDAO.save(permission);
        objectPermissions.add(permission);
        object = objectDAO.save(object);
        return object;
    }

    @Override
    public boolean canReadObject(final String objectId, final String username) {
        return canExecuteOperation(objectId, username, "read");
    }

    @Override
    public boolean canCreateObject(final String objectId, final String username) {
        return canExecuteOperation(objectId, username, "create");
    }

    @Override
    public boolean canUpdateObject(final String objectId, final String username) {
        return canExecuteOperation(objectId, username, "update");
    }

    @Override
    public boolean canDeleteObject(final String objectId, final String username) {
        return canExecuteOperation(objectId, username, "delete");
    }

    private boolean canExecuteOperation(final String objectId, final String username, final String operationName) {
        final User user = userDAO.findByUsername(username);
        final Object object = objectDAO.findByName(objectId);
        if (user == null || object == null) {
            return false;
        }
        final Set<Role> userRoles = user.getRoles();
        if (userRoles.size() == 0) {
            return false;
        }
        final Set<Permission> objectPermissions = object.getPermissions();
        for (final Permission objectPermission : objectPermissions) {
            final Set<Role> objectPermissionRoles = objectPermission.getRoles();
            final Set<Role> intersectedRoles = Sets.intersection(objectPermissionRoles, userRoles);
            if (intersectedRoles.size() > 0) {
                final Operation permissionOperation = objectPermission.getPermissionOperation();
                if (permissionOperation != null) {
                    if (operationName.equals("create") && permissionOperation.getCanCreate() == true) {
                        return true;
                    } else if (operationName.equals("delete") && permissionOperation.getCanDelete() == true) {
                        return true;
                    } else if (operationName.equals("update") && permissionOperation.getCanUpdate() == true) {
                        return true;
                    } else if (operationName.equals("read") && permissionOperation.getCanRead() == true) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public User getUser(final String username, final String password) {
        return userDAO.findByUsernameAndPassword(username, password);
    }

    @Override
    public User getUser(final String username) {
        return userDAO.findByUsername(username);
    }

    @Override
    public List<String> getRoleNamesForUser(final String username) {
        final User user = getUser(username);
        final List<String> roleNames = new ArrayList<String>();
        if (user != null) {
            final Set<Role> roles = user.getRoles();
            for (final Role role : roles) {
                roleNames.add(role.getRoleName());
            }
            LOGGER.error("Role names " + roleNames.toString());
        }
        return roleNames;
    }
}
